package logic;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;

public class CircuitFactory
{
	private Circuit circuit=null;
	private static CircuitFactory factory=null;
	private HashMap<String,QVAR> variables=new HashMap<String,QVAR>();
	private HashMap<String,QVAR> operate_variables=new HashMap<String,QVAR>();
	private HashMap<String,GATE> defined_gates=new HashMap<String,GATE>();
	private int step=0;
	private GateBlock tmpBlock=new GateBlock(0);
	
	private CircuitFactory()
	{
		circuit=new Circuit();
	}
	public static CircuitFactory getInstance()
	{
		if(factory==null)
		 factory=new CircuitFactory();
		return factory; 
	}	
	private void addOperateVariable(QVAR targ_bit)
    {
	   if(!operate_variables.containsKey(targ_bit.getName()))
		 {
		  circuit.addVar(targ_bit);
		  operate_variables.put(targ_bit.getName(),targ_bit);
		 }
    }
	public void addOperateVariable(String varname)
	{
		//System.out.println("New qubit "+varname);
		if(varname.contains("["))
		{
			String s=varname;
			s=s.replace('[','_');
			s=s.replace(']','_');
			String[] parts = s.split("_");
			String name=parts[0];
			int range=Integer.parseInt(parts[1]);
			for(int i=1;i<=range;i++)
			 {
				 String v=name+"["+i+"]";
				 variables.put(v,new QVAR(v));
			 } 
		}
		else
		{
			variables.put(varname,new QVAR(varname));
		}		
	}
	public void addGateDefinition(String gateName,double v1,double v2,double v3,double v4)
	{
		//System.out.printf("New gate %s %f %f %f %f\n",gateName,v1,v2,v3,v4);
		defined_gates.put(gateName,new GATE(gateName,v1,v2,v3,v4));
		circuit.addGateDefinition(new GATE(gateName,v1,v2,v3,v4));		
	}
	
	public void addImplementingGate(ArrayList<String> vars)
	{
		if(vars.size()==2)
		 {
			 //System.out.println("Gate "+vars.get(0)+" "+vars.get(1));
			 addNormalGate(vars.get(0),vars.get(1));
		 }
		if(vars.size()==3)
		 {
			// System.out.println("Gate Control "+vars.get(0)+" "+vars.get(1)+" "+vars.get(2));
			 addControlGate(vars.get(0),vars.get(1),vars.get(2));
		 }
		if(vars.size()==4)
		 {
			 //System.out.println("Gate Control Control "+vars.get(0)+" "+vars.get(1)+" "+vars.get(2)+" "+vars.get(3));  
			 addControlControlGate(vars.get(0),vars.get(1),vars.get(2),vars.get(3)); 
		 }	 
	}
	private void addNormalGate(String gate,String qubit)
	{
		QVAR targ_bit1=variables.get(qubit);
		GATE qgate1;
		if(GATELIB.isInLibrary(gate))
		 qgate1=GATELIB.getLibGate(gate);
		else 
		{
		 qgate1=defined_gates.get(gate);
		 qgate1=qgate1.copy();
		}
		qgate1.connectQubits(targ_bit1,null);
		if(tmpBlock!=null)
		  tmpBlock.addGate(qgate1);
		addOperateVariable(targ_bit1);
	}
	private void addControlGate(String gate,String cntrlBit,String targBit)
	{
		QVAR ctr_bit=variables.get(cntrlBit);
		QVAR targ_bit=variables.get(targBit);
		GATE qgate;
		if(GATELIB.isInLibrary(gate))
		 {
			 qgate=GATELIB.getLibGate(gate);
			 if(tmpBlock!=null)
				tmpBlock.addGate(qgate);
		 }
		else 
		{
		 qgate=defined_gates.get(gate);
		 qgate=qgate.copy();
		} 
		QVAR[] control_bits=new QVAR[1];
		control_bits[0]=ctr_bit;
		qgate.connectQubits(targ_bit,control_bits);
		 
		if(!GATELIB.isInLibrary(gate))
		{
			GATE[] implement_gate=qgate.one_control_decompose();
			for(GATE g:implement_gate)
			{
				circuit.addGateDefinition(g);
			} 
			if(tmpBlock!=null)
			tmpBlock.addGates(implement_gate);
		}	
		addOperateVariable(targ_bit);
		addOperateVariable(ctr_bit);
	}
	private void addControlControlGate(String gate,String cntrlBit1,String cntrlBit2,String targBit)
	{
		QVAR targ_bit=variables.get(targBit);
        QVAR ctr_bit1=variables.get(cntrlBit1);
        QVAR ctr_bit2=variables.get(cntrlBit2);
        QVAR[] control=new QVAR[2];
        control[0]=ctr_bit1;
        control[1]=ctr_bit2;
        GATE qgate=defined_gates.get(gate);
         
        qgate.connectQubits(targ_bit,control);
        GATE[] implement_gate=qgate.two_control_decompose();
		for(GATE g:implement_gate)
		 {
			 circuit.addGateDefinition(g);
		 } 
		if(tmpBlock!=null)
		  tmpBlock.addGates(implement_gate);
		addOperateVariable(targ_bit);
		addOperateVariable(ctr_bit1);
		addOperateVariable(ctr_bit2);
	}
				
	public void enterNewBlock()
	{
		//System.out.println("New block");
		tmpBlock=new GateBlock(step);	
		circuit.addGateBlock(tmpBlock);
		step++;
	}				
	public void decompose1()
	{
		circuit.decompose1();
	}	
	public void output()
	{
		circuit.output();
	}	
}
	
